/**
 * 匹配队列管理模块
 * 设计一个匹配队列类，负责管理用户匹配
 * 设计匹配管理类，管理匹配队列，根据用户水平，创建不同的匹配队列进行匹配
 * 设计三个不同层次的匹配队列，让三个线程去负责玩家匹配
 */

#pragma once 
#include"Detail.hpp"
#include<list>
#include<condition_variable>
#include<mutex>
#include<thread>
#include"RoomManager.hpp"
#include"DataBase.hpp"
#include"OnlineManager.hpp"


namespace OnlineGoBang
{
    //匹配队列
    template<class T>
    class MatchQueue
    {
    public:
        //元素数量
        int Size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _list.size();
        }
        //判断是否为空
        bool Empty()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _list.empty();
        }
        //阻塞线程
        void Wait()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _cond.wait(lock);
        }
        //出队
        bool Pop(T& data)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if(_list.empty())
            {
                return false;
            }
            data=_list.front();
            _list.pop_front();
            return true;
        }
        //入队，并唤醒线程
        void Push(const T& data)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _list.push_back(data);
            _cond.notify_all();
        }
        //移除指定数据
        void Remove(const T& data)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _list.remove(data);
        }

    private:
        std::list<T> _list;//模拟队列，不用queue是因为需要对中间数据进行操作
        std::mutex _mutex;
        std::condition_variable _cond;//条件变量
    };

    using MatchQueuePtr=std::shared_ptr<MatchQueue<uint64_t>>;
    using RoomManagerPtr=std::shared_ptr<RoomManager>;

    //匹配管理模块
    class MatchManager
    {
        //具体的匹配处理
        void HandlerMatch(MatchQueue<uint64_t>& q)
        {
            while(1)
            {
                //等待队列中用户数量大于等于2
                while(q.Size()<2)
                {
                    q.Wait();
                }
                //取出两个用户
                uint64_t u1,u2;
                bool ret=q.Pop(u1);
                if(!ret)
                {
                    continue;
                }
                ret=q.Pop(u2);
                if(!ret)
                {
                    //q.Push(u1);
                    Add(u1);
                    continue;
                }
                //检查两个用户是否都在大厅,同时获取连接
                wsserver_t::connection_ptr con1=_omp->GetConnectionFromHall(u1);
                if(con1.get()==nullptr)
                {
                    Add(u2);
                    continue;
                }
                wsserver_t::connection_ptr con2=_omp->GetConnectionFromHall(u2);
                if(con2.get()==nullptr)
                {
                    Add(u1);
                    continue;
                }
                //创建房间
                RoomPtr rp = _rmp->CreateRoom(u1,u2);
                if(rp.get()==nullptr)
                {
                    Add(u1);
                    Add(u2);
                    continue;
                }
                //对玩家进行响应
                Json::Value resp;
                resp["optype"]="match_success";
                resp["result"]=true;
                std::string body;
                Detail::JSON::Serialize(resp,body);
                con1->send(body);
                con2->send(body);
            }
        }

        //线程回调函数
        void th_normal()
        {
            HandlerMatch(_q_normal);
        }
        void th_master()
        {
            HandlerMatch(_q_master);
        }
        void th_super()
        {
            HandlerMatch(_q_super);
        }
    public:
        MatchManager(RoomManager* rm,UserTable* ut,OnlineManager* om)
            :_rmp(rm),_utp(ut),_omp(om)
            ,_t_normal(std::thread(&MatchManager::th_normal,this))
            ,_t_master(std::thread(&MatchManager::th_master,this))
            ,_t_super(std::thread(&MatchManager::th_super,this))
        {}
        MatchManager(RoomManagerPtr& rm,UserTablePtr& ut,OnlineManagerPtr& om)
            :_rmp(rm),_utp(ut),_omp(om)
            ,_t_normal(std::thread(&MatchManager::th_normal,this))
            ,_t_master(std::thread(&MatchManager::th_master,this))
            ,_t_super(std::thread(&MatchManager::th_super,this))
        {}
        //添加用户
        bool Add(const uint64_t& uid)
        {
            //根据用户ID获取用户分数
            Json::Value user;
            bool ret=_utp->Select_by_ID(uid,user);
            if(!ret)
            {
                LOG(ERROR,"获取用户%lu信息失败\n",uid);
                return false;
            }
            //根据分数让玩家入队匹配
            int score=user["score"].asInt();
            if(score<2000)
            {
                _q_normal.Push(uid);
            }
            else if(score>=2000&&score<3000)
            {
                _q_master.Push(uid);
            }
            else
            {
                _q_super.Push(uid);
            }
            return true;
        }
        //删除用户
        bool Del(const uint64_t& uid)
        {
            //根据用户ID获取用户分数
            Json::Value user;
            bool ret=_utp->Select_by_ID(uid,user);
            if(!ret)
            {
                LOG(ERROR,"获取用户%lu信息失败\n",uid);
                return false;
            }
            //根据玩家分数，去对应的队列中移除玩家
            int score=user["score"].asInt();
            if(score<2000)
            {
                _q_normal.Remove(uid);
            }
            else if(score>=2000&&score<3000)
            {
                _q_master.Remove(uid);
            }
            else
            {
                _q_super.Remove(uid);
            }
            return true;
        }
        ~MatchManager()
        {
            _t_normal.join();
            _t_master.join();
            _t_super.join();
        }
    private:
        MatchQueue<uint64_t> _q_normal;//普通玩家匹配队列
        MatchQueue<uint64_t> _q_master;//高手玩家匹配队列
        MatchQueue<uint64_t> _q_super;//大神玩家匹配队列
        std::thread _t_normal;//管理普通玩家匹配
        std::thread _t_master;//管理高手玩家匹配
        std::thread _t_super;//管理大神玩家匹配
        RoomManagerPtr _rmp;//房间管理
        UserTablePtr _utp;//用户数据管理
        OnlineManagerPtr _omp;//在线用户管理
    };
}